区块链学习笔记之BalsnCTF 2020 IdleGame
这道题的代码还蛮多的,为了不太占篇幅,于是把相关代码塞到仓库里了。
首先看到解题条件叭
1 | contract Setup { |
只有IDL才能调用giveMeFlag(),那么看到IDL的合约,有一个
1 | function giveMeFlag() public { |
IDL本身是一个继承ERC20的代币合约,也就是我们需要拥有(10 ** 8) * scale 个 IDL代币 (scale的值是10^18)
那看看哪里能搞到币。
1 | function buyGamePoints(uint amount) public returns (uint) { |
可以看到这里有buyGamePoints 和 sellGamePoints函数,直觉上来说,这里会不会有一种赚差价的方式,让钱生钱,利滚利。那初始资金呢哪儿来呢?可以注意到这里的买卖还用了另外一个代币BSN,看到BSN的合约
1 | contract BalsnToken is ERC20 { |
有一个giveMeMoney,如果你没钱,就会给你一块BSN。
这个时候就有一个想法,我们是不是可以领一块BSN,然后拿去买IDL。那么需要想办法看看一块BSN能买多少个IDL。
重新回到上面的 buyGamePoints 函数,里头有一个 uint bought = _continuousMint(amount);
,这个amount是BSN的值,bought是可以买到的IDL,那么显然这个_continuousMint
是一个类似于汇率的东西。跟踪到 ContinuousToken 的合约代码,在Tokens.sol里,
1 | function calculateContinuousMintReturn(uint _amount) public view returns (uint mintAmount) { |
_continuousMint -> calculateContinuousMintReturn -> BBC.calculatePurchaseReturn(totalSupply(), reserveBalance, reserveRatio, _amount);
这个BBC是啥,可以看到上面,
BancorBondingCurve public constant BBC = BancorBondingCurve(0xF88212805fE6e37181DE56440CF350817FF87130);
好像是条曲线。去到这个地址发现是提供源码的,就是文件夹里的BBC.sol。挺长的,不是很能看懂规则。
既然详细的规则看不懂,我们直接部署看结果好了。
首先部署BBC.sol,然后拿到BBC的地址,把Tokens里的地址替换调,然后部署Setup,通过Setup拿到IDL的地址,用地址获取IDL的合约实例
部署完了会有这样三个实例,我们直接调用IDL的 calculateContinuousMintReturn 函数
发现一个BSN能换36192100个IDL,还是挺多的?但是计算一下,以这个价格至少需要414455088265118631个BSN,而且还是没考虑货币增值的情况。一个很直观的认知应该是,买的越多,这个东西就变得越贵。我们看到Setup合约的构造函数中有
1 | constructor() public { |
BSN买了 15000000 * (10 ** 18) 个IDL,所以有了这么个价格,那么我们把这个值改的小一点,看看这个价格会是多少。
当改成IDL.buyGamePoints(15000000);
后
一个BSN能买37933028个IDL。果然是变便宜了。所以我们的认知还是比较正确的。(但是知道了这个结果有什么用的。不过是更加说明了我们嫖一个BSN,拿去买IDL,再嫖,再买的方式的行不通罢了)
回到最初的想法,我们找找是否有一个买卖赚差价的方式。也就是在一买和一卖之间,我们的BSN有增值,或者IDL有贬值。
我们知道,IDL被买的越多,就越贵,所以能不能在我们买入IDL的时候,有别人大量的抛售IDL呢?但是持有IDL的别人只有BSN这个合约,我们又没法去控制它。但除了抛出IDL会让货币贬值,另一个让货币贬值的方式,就是货币的增发。(所谓通货膨胀)那如何让货币增发呢?注意到IDL还是一个闪电贷(FlashERC20 )的代币。
1 | contract FlashERC20 is ERC20 { |
可以看到,我们走一波闪电贷的时候,他会向我的账户直接增发amount个代币,然后执行我们的一个executeOnFlashMint(amount)
方法,然后销毁。而这个增发,会不会真的如我们所想影响代币的价值呢?我们继续测,我们手动给这个FlashERC20加一个mint 和 burn方法,其实就是把flashMint的操作给拆分了。好让我们有时间去手动查看汇率。
1 | function Mint(uint amount) external { |
我们就给自己先增发个15000000 * (10 ** 18)个代币看看,然后看看汇率
可以发现这个汇率已经起飞了。然后把增发的burn掉,汇率又回去了。
所以思路就很清楚了。
- 我们调用闪电贷,先获取大额的IDL代币
- 闪电贷会调用我们的一个executeOnFlashMint(amount)方法,我们就用这个方法领一个BSN,然后去买到特别多的IDL代币
- 闪电贷结束,大额的IDL代币被销毁,汇率回归,我们把手里的IDL代币全抛了换回BSN
这样一次下来我们应该就能赚到不少的BSN,多搞几次,手里的BSN就是呈指数型增长。最后IDL够了,就可以获取flag了。
攻击合约(zbr的)
1 | pragma solidity =0.5.17; |
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可联系QQ 643713081,也可以邮件至 643713081@qq.com
文章标题:区块链学习笔记之BalsnCTF 2020 IdleGame
文章字数:1.6k
本文作者:Van1sh
发布时间:2022-02-15, 15:39:00
最后更新:2022-02-16, 22:49:03
原始链接:http://jayxv.github.io/2022/02/15/区块链学习笔记之BalsnCTF 2020 IdleGame/版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。